#!/usr/bin/env python3
# Copyright (C) 2018 Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
#
# This is free software, licensed under the GNU General Public License v3.0.
# See /LICENSE for more information.
#

import json
import base64
import uuid
import configparser
import subprocess
import os
import socket
import re
import hashlib
import time
from datetime import timedelta
from tempfile import mkstemp
from shutil import move
from pprint import pprint
from netjsonconfig import OpenWrt
from flask import Flask, jsonify, request, session
from flask_jwt_simple import (
    JWTManager, jwt_required, create_jwt, get_jwt_identity
)

app = Flask(__name__)

import logging
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
# Setup the Flask-JWT-Simple extension

# Generate a random secret key
app.config['SECRET_KEY'] = uuid.uuid4().hex
app.config['JWT_SECRET_KEY'] = uuid.uuid4().hex
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=24)

jwt = JWTManager(app)

# Get main net interface
file = open('/etc/shorewall/params.net', "r")
read = file.read()
iface = None
for line in read.splitlines():
    if 'NET_IFACE=' in line:
        iface=line.split('=',1)[1]

# Get interface rx/tx
def get_bytes(t, iface='eth0'):
    with open('/sys/class/net/' + iface + '/statistics/' + t + '_bytes', 'r') as f:
        data = f.read();
    return int(data)

def ordered(obj):
    if isinstance(obj, dict):
        return sorted((k, ordered(v)) for k, v in obj.items())
    if isinstance(obj, list):
        return sorted(ordered(x) for x in obj)
    else:
        return obj

def file_as_bytes(file):
    with file:
        return file.read()

def shorewall_add_port(port,proto,name,fwtype='ACCEPT'):
    initial_md5 = hashlib.md5(file_as_bytes(open('/etc/shorewall/rules', 'rb'))).hexdigest()
    fd, tmpfile = mkstemp()
    with open('/etc/shorewall/rules','r') as f, open(tmpfile,'a+') as n:
        for line in f:
            if fwtype == 'ACCEPT' and not port + '	# OMR open ' + name + ' port ' + proto in line:
                n.write(line)
            elif fwtype == 'DNAT' and not port + '	# OMR redirect ' + name + ' port ' + proto in line:
                n.write(line)
        if fwtype == 'ACCEPT':
            n.write('ACCEPT		net		$FW		' + proto + '	' + port + '	# OMR open ' + name + ' port ' + proto + "\n")
        elif fwtype == 'DNAT':
            n.write('DNAT		net		vpn:$OMR_ADDR	' + proto + '	' + port + '	# OMR redirect ' + name + ' port ' + proto + "\n")
    os.close(fd)
    move(tmpfile,'/etc/shorewall/rules')
    final_md5 = hashlib.md5(file_as_bytes(open('/etc/shorewall/rules', 'rb'))).hexdigest()
    if not initial_md5 == final_md5:
        os.system("systemctl -q reload shorewall")

def shorewall_del_port(port,proto,name,fwtype='ACCEPT'):
    initial_md5 = hashlib.md5(file_as_bytes(open('/etc/shorewall/rules', 'rb'))).hexdigest()
    fd, tmpfile = mkstemp()
    with open('/etc/shorewall/rules','r') as f, open(tmpfile,'a+') as n:
        for line in f:
            if fwtype == 'ACCEPT' and not port + '	# OMR open ' + name + ' port ' + proto in line:
                n.write(line)
            elif fwtype == 'DNAT' and not port + '	# OMR redirect ' + name + ' port ' + proto in line:
                n.write(line)
    os.close(fd)
    move(tmpfile,'/etc/shorewall/rules')
    final_md5 = hashlib.md5(file_as_bytes(open('/etc/shorewall/rules', 'rb'))).hexdigest()
    if not initial_md5 == final_md5:
        os.system("systemctl -q reload shorewall")

def set_lastchange():
    with open('/etc/openmptcprouter-vps-admin/omr-admin-config.json') as f:
        content = f.read()
    content = re.sub(",\s*}","}",content)
    try:
        data = json.loads(content)
    except ValueError as e:
        return jsonify({'error': 'Config file not readable','route': 'lastchange'}), 200
    data["lastchange"] = time.time()
    with open('/etc/openmptcprouter-vps-admin/omr-admin-config.json','w') as outfile:
        json.dump(data,outfile,indent=4)


# Provide a method to create access tokens. The create_jwt()
# function is used to actually generate the token
@app.route('/login', methods=['POST'])
def login():
    if not request.is_json:
        return jsonify({"msg": "Missing JSON in request"}), 400

    session.permanent = True
    with open('/etc/openmptcprouter-vps-admin/omr-admin-config.json') as f:
        omr_config_data = json.load(f)

    params = request.get_json()
    username = params.get('username', None)
    password = params.get('password', None)

    if not username:
        return jsonify({"msg": "Missing username parameter"}), 400
    if not password:
        return jsonify({"msg": "Missing password parameter"}), 400

    if username != omr_config_data["user"] or password != omr_config_data["pass"]:
        return jsonify({"msg": "Bad username or password"}), 401

    # Identity can be any data that is json serializable
    ret = {'token': create_jwt(identity=username)}
    return jsonify(ret), 200


# Get VPS status
@app.route('/status', methods=['GET'])
@jwt_required
def status():
    vps_loadavg = os.popen("cat /proc/loadavg | awk '{print $1\" \"$2\" \"$3}'").read().rstrip()
    vps_uptime = os.popen("cat /proc/uptime | awk '{print $1}'").read().rstrip()
    vps_hostname = socket.gethostname()
    mptcp_enabled = os.popen('sysctl -n net.mptcp.mptcp_enabled').read().rstrip()

    if iface:
        return jsonify({'vps': {'loadavg': vps_loadavg,'uptime': vps_uptime,'mptcp': mptcp_enabled,'hostname': vps_hostname}, 'network': {'tx': get_bytes('tx',iface),'rx': get_bytes('rx',iface)}}), 200
    else:
        return jsonify({'error': 'No iface defined','route': 'status'}), 200

# Get VPS config
@app.route('/config', methods=['GET'])
@jwt_required
def config():
    with open('/etc/openmptcprouter-vps-admin/omr-admin-config.json') as f:
        try:
            omr_config_data = json.load(f)
        except ValueError as e:
            omr_config_data = {}
    with open('/etc/shadowsocks-libev/config.json') as f:
        content = f.read()
    content = re.sub(",\s*}","}",content)
    try:
        data = json.loads(content)
    except ValueError as e:
        data = {'key': '', 'server_port': 65101, 'method': 'chacha20'}
    shadowsocks_key = data["key"]
    shadowsocks_port = data["server_port"]
    shadowsocks_method = data["method"]
    if 'fast_open' in data:
        shadowsocks_fast_open = data["fast_open"]
    else:
        shadowsocks_fast_open = False
    if 'reuse_port' in data:
        shadowsocks_reuse_port = data["reuse_port"]
    else:
        shadowsocks_reuse_port = False
    if 'no_delay' in data:
        shadowsocks_no_delay = data["no_delay"]
    else:
        shadowsocks_no_delay = False
    if 'mptcp' in data:
        shadowsocks_mptcp = data["mptcp"]
    else:
        shadowsocks_mptcp = False
    if 'ebpf' in data:
        shadowsocks_ebpf = data["ebpf"]
    else:
        shadowsocks_ebpf = False
    if "plugin" in data:
        shadowsocks_obfs = True
        if 'v2ray' in data["plugin"]:
            shadowsocks_obfs_plugin = 'v2ray'
        else:
            shadowsocks_obfs_plugin = 'obfs'
        if 'tls' in data["plugin_opts"]:
            shadowsocks_obfs_type = 'tls'
        else:
            shadowsocks_obfs_type = 'http'
    else:
        shadowsocks_obfs = False
        shadowsocks_obfs_plugin = ''
        shadowsocks_obfs_type = ''
    if os.path.isfile('/etc/glorytun-tcp/tun0.key'):
        glorytun_key = open('/etc/glorytun-tcp/tun0.key').readline().rstrip()
    else:
        glorytun_key = ''
    glorytun_port = '65001'
    glorytun_chacha = False
    if os.path.isfile('/etc/glorytun-tcp/tun0'):
        with open('/etc/glorytun-tcp/tun0',"r") as glorytun_file:
            for line in glorytun_file:
                if 'PORT=' in line:
                    glorytun_port = line.replace(line[:5], '').rstrip()
                if 'chacha' in line:
                    glorytun_chacha = True
    if 'glorytun_tcp_type' in omr_config_data:
        if omr_config_data['glorytun_tcp_type'] == 'static':
            glorytun_tcp_host_ip = '10.255.255.1'
            glorytun_tcp_client_ip = '10.255.255.2'
        else:
            glorytun_tcp_host_ip = 'dhcp'
            glorytun_tcp_client_ip = 'dhcp'
    else:
        glorytun_tcp_host_ip = '10.255.255.1'
        glorytun_tcp_client_ip = '10.255.255.2'
    if 'glorytun_udp_type' in omr_config_data:
        if omr_config_data['glorytun_udp_type'] == 'static':
            glorytun_udp_host_ip = '10.255.254.1'
            glorytun_udp_client_ip = '10.255.254.2'
        else:
            glorytun_udp_host_ip = 'dhcp'
            glorytun_udp_client_ip = 'dhcp'
    else:
        glorytun_udp_host_ip = '10.255.254.1'
        glorytun_udp_client_ip = '10.255.254.2'
    available_vpn = ["glorytun-tcp", "glorytun-udp"]
    if os.path.isfile('/etc/dsvpn/dsvpn.key'):
        dsvpn_key = open('/etc/dsvpn/dsvpn.key').readline().rstrip()
        available_vpn.append("dsvpn")
    else:
        dsvpn_key = ''
    dsvpn_port = '65011'
    dsvpn_host_ip = '10.255.251.1'
    dsvpn_client_ip = '10.255.251.2'

    if os.path.isfile('/etc/iperf3/public.pem'):
        with open('/etc/iperf3/public.pem',"rb") as iperfkey_file:
            iperf_keyb = base64.b64encode(iperfkey_file.read())
            iperf3_key = iperf_keyb.decode('utf-8')
    else:
        iperf3_key = ''

    if os.path.isfile('/etc/pihole/setupVars.conf'):
        pihole = True
    else:
        pihole = False

    if os.path.isfile('/etc/openvpn/server/static.key'):
        with open('/etc/openvpn/server/static.key',"rb") as ovpnkey_file:
            openvpn_keyb = base64.b64encode(ovpnkey_file.read())
            openvpn_key = openvpn_keyb.decode('utf-8')
        available_vpn.append("openvpn")
    else:
        openvpn_key = ''
    if os.path.isfile('/etc/openvpn/client/client.key'):
        with open('/etc/openvpn/client/client.key',"rb") as ovpnkey_file:
            openvpn_keyb = base64.b64encode(ovpnkey_file.read())
            openvpn_client_key = openvpn_keyb.decode('utf-8')
    else:
        openvpn_client_key = ''
    if os.path.isfile('/etc/openvpn/client/client.crt'):
        with open('/etc/openvpn/client/client.crt',"rb") as ovpnkey_file:
            openvpn_keyb = base64.b64encode(ovpnkey_file.read())
            openvpn_client_crt = openvpn_keyb.decode('utf-8')
    else:
        openvpn_client_crt = ''
    if os.path.isfile('/etc/openvpn/server/ca.crt'):
        with open('/etc/openvpn/server/ca.crt',"rb") as ovpnkey_file:
            openvpn_keyb = base64.b64encode(ovpnkey_file.read())
            openvpn_client_ca = openvpn_keyb.decode('utf-8')
    else:
        openvpn_client_ca = ''
    openvpn_port = '65301'
    if os.path.isfile('/etc/openvpn/openvpn-tun0.conf'):
        with open('/etc/openvpn/openvpn-tun0.conf',"r") as openvpn_file:
            for line in openvpn_file:
                if 'port ' in line:
                    openvpn_port = line.replace(line[:5], '').rstrip()
    openvpn_host_ip = '10.255.252.1'
    openvpn_client_ip = '10.255.252.2'
    #openvpn_client_ip = 'dhcp'

    if os.path.isfile('/etc/mlvpn/mlvpn0.conf'):
        mlvpn_config = configparser.ConfigParser()
        mlvpn_config.readfp(open(r'/etc/mlvpn/mlvpn0.conf'))
        mlvpn_key = mlvpn_config.get('general','password').strip('"')
        available_vpn.append("mlvpn")
    else:
        mlvpn_key = ''
    mlvpn_host_ip = '10.255.253.1'
    mlvpn_client_ip = '10.255.253.2'


    mptcp_enabled = os.popen('sysctl -n net.mptcp.mptcp_enabled').read().rstrip()
    mptcp_checksum = os.popen('sysctl -n net.mptcp.mptcp_checksum').read().rstrip()
    mptcp_path_manager = os.popen('sysctl -n  net.mptcp.mptcp_path_manager').read().rstrip()
    mptcp_scheduler = os.popen('sysctl -n net.mptcp.mptcp_scheduler').read().rstrip()
    mptcp_syn_retries = os.popen('sysctl -n net.mptcp.mptcp_syn_retries').read().rstrip()

    congestion_control = os.popen('sysctl -n net.ipv4.tcp_congestion_control').read().rstrip()

    ipv6_network = os.popen('ip -6 addr show ' + iface +' | grep -oP "(?<=inet6 ).*(?= scope global)"').read().rstrip()
    #ipv6_addr = os.popen('wget -6 -qO- -T 2 ipv6.openmptcprouter.com').read().rstrip()
    ipv6_addr = os.popen('ip -6 addr show ' + iface +' | grep -oP "(?<=inet6 ).*(?= scope global)" | cut -d/ -f1').read().rstrip()
    #ipv4_addr = os.popen('wget -4 -qO- -T 1 https://ip.openmptcprouter.com').read().rstrip()
    ipv4_addr = os.popen("dig -4 TXT +timeout=2 +tries=1 +short o-o.myaddr.l.google.com @ns1.google.com | awk -F'\"' '{ print $2}'").read().rstrip()
    if ipv4_addr == '':
        ipv4_addr = os.popen('wget -4 -qO- -T 1 http://ifconfig.co').read().rstrip()
    #ipv4_addr = ""

    test_aes = os.popen('cat /proc/cpuinfo | grep aes').read().rstrip()
    if test_aes == '':
        vps_aes = False
    else:
        vps_aes = True
    vps_kernel = os.popen('uname -r').read().rstrip()
    vps_machine = os.popen('uname -m').read().rstrip()
    vps_omr_version = os.popen("grep -s 'OpenMPTCProuter VPS' /etc/* | awk '{print $4}'").read().rstrip()
    vps_loadavg = os.popen("cat /proc/loadavg | awk '{print $1" "$2" "$3}'").read().rstrip()
    vps_uptime = os.popen("cat /proc/uptime | awk '{print $1}'").read().rstrip()
    vps_domain = os.popen('wget -4 -qO- -T 1 http://hostname.openmptcprouter.com').read().rstrip()
    #vps_domain = os.popen('dig -4 +short +times=3 +tries=1 -x ' + ipv4_addr + " | sed 's/\.$//'").read().rstrip()

    vpn = ''
    if os.path.isfile('/etc/openmptcprouter-vps-admin/current-vpn'):
        vpn = os.popen('cat /etc/openmptcprouter-vps-admin/current-vpn').read().rstrip()
    if vpn == '':
        vpn = 'glorytun-tcp'

    shorewall_redirect = "enable"
    with open('/etc/shorewall/rules','r') as f:
        for line in f:
            if '#DNAT		net		vpn:$OMR_ADDR	tcp	1-64999' in line:
                shorewall_redirect = "disable"

    return jsonify({'vps': {'kernel': vps_kernel,'machine': vps_machine,'omr_version': vps_omr_version,'loadavg': vps_loadavg,'uptime': vps_uptime,'aes': vps_aes},'shadowsocks': {'key': shadowsocks_key,'port': shadowsocks_port,'method': shadowsocks_method,'fast_open': shadowsocks_fast_open,'reuse_port': shadowsocks_reuse_port,'no_delay': shadowsocks_no_delay,'mptcp': shadowsocks_mptcp,'ebpf': shadowsocks_ebpf,'obfs': shadowsocks_obfs,'obfs_plugin': shadowsocks_obfs_plugin,'obfs_type': shadowsocks_obfs_type},'glorytun': {'key': glorytun_key,'udp': {'host_ip': glorytun_udp_host_ip,'client_ip': glorytun_udp_client_ip},'tcp': {'host_ip': glorytun_tcp_host_ip,'client_ip': glorytun_tcp_client_ip},'port': glorytun_port,'chacha': glorytun_chacha},'dsvpn': {'key': dsvpn_key, 'host_ip': dsvpn_host_ip, 'client_ip': dsvpn_client_ip, 'port': dsvpn_port},'openvpn': {'key': openvpn_key,'client_key': openvpn_client_key,'client_crt': openvpn_client_crt,'client_ca': openvpn_client_ca,'host_ip': openvpn_host_ip, 'client_ip': openvpn_client_ip, 'port': openvpn_port},'mlvpn': {'key': mlvpn_key, 'host_ip': mlvpn_host_ip, 'client_ip': mlvpn_client_ip},'shorewall': {'redirect_ports': shorewall_redirect},'mptcp': {'enabled': mptcp_enabled,'checksum': mptcp_checksum,'path_manager': mptcp_path_manager,'scheduler': mptcp_scheduler, 'syn_retries': mptcp_syn_retries},'network': {'congestion_control': congestion_control,'ipv6_network': ipv6_network,'ipv6': ipv6_addr,'ipv4': ipv4_addr,'domain': vps_domain},'vpn': {'available': available_vpn,'current': vpn},'iperf': {'user': 'openmptcprouter','password': 'openmptcprouter', 'key': iperf3_key},'pihole': {'state': pihole}}), 200

# Set shadowsocks config
@app.route('/shadowsocks', methods=['POST'])
@jwt_required
def shadowsocks():
    with open('/etc/shadowsocks-libev/config.json') as f:
        content = f.read()
    content = re.sub(",\s*}","}",content)
    try:
        data = json.loads(content)
    except ValueError as e:
        data = {'timeout': 600, 'verbose': 0, 'prefer_ipv6': False}
    #key = data["key"]
    if 'timeout' in data:
        timeout = data["timeout"]
    if 'verbose' in data:
        verbose = data["verbose"]
    else:
        verbose = 0
    prefer_ipv6 = data["prefer_ipv6"]
    params = request.get_json()
    port = params.get('port', None)
    method = params.get('method', None)
    fast_open = params.get('fast_open', None)
    reuse_port = params.get('reuse_port', None)
    no_delay = params.get('no_delay', None)
    mptcp = params.get('mptcp', None)
    obfs = params.get('obfs', False)
    obfs_plugin = params.get('obfs_plugin', False)
    obfs_type = params.get('obfs_type', None)
    ebpf = params.get('ebpf', False)
    key = params.get('key', None)
    if not key:
        if 'key' in data:
            key = data["key"]
    #ipv4_addr = os.popen('wget -4 -qO- -T 2 http://ip.openmptcprouter.com').read().rstrip()
    vps_domain = os.popen('wget -4 -qO- -T 2 http://hostname.openmptcprouter.com').read().rstrip()

    if port is None or method is None or fast_open is None or reuse_port is None or no_delay is None or key is None:
        return jsonify({'result': 'error','reason': 'Invalid parameters','route': 'shadowsocks'})
    if obfs:
        if obfs_plugin == 'v2ray':
            if obfs_type == 'tls':
                if vps_domain == '':
                    shadowsocks_config = {'server': '::0','server_port': port,'local_port': 1081,'mode': 'tcp_and_udp','key': key,'timeout': timeout,'method': method,'verbose': verbose,'ipv6_first': True, 'prefer_ipv6': prefer_ipv6,'fast_open': fast_open,'no_delay': no_delay,'reuse_port': reuse_port,'mptcp': mptcp,'ebpf': ebpf,'acl': '/etc/shadowsocks-libev/local.acl', 'plugin': '/usr/local/bin/v2ray-plugin','plugin_opts': 'server;tls'}
                else:
                    shadowsocks_config = {'server': '::0','server_port': port,'local_port': 1081,'mode': 'tcp_and_udp','key': key,'timeout': timeout,'method': method,'verbose': verbose,'ipv6_first': True, 'prefer_ipv6': prefer_ipv6,'fast_open': fast_open,'no_delay': no_delay,'reuse_port': reuse_port,'mptcp': mptcp,'ebpf': ebpf,'acl': '/etc/shadowsocks-libev/local.acl', 'plugin': '/usr/local/bin/v2ray-plugin','plugin_opts': 'server;tls;host=' + vps_domain}
            else:
                shadowsocks_config = {'server': '::0','server_port': port,'local_port': 1081,'mode': 'tcp_and_udp','key': key,'timeout': timeout,'method': method,'verbose': verbose,'ipv6_first': True, 'prefer_ipv6': prefer_ipv6,'fast_open': fast_open,'no_delay': no_delay,'reuse_port': reuse_port,'mptcp': mptcp,'ebpf': ebpf,'acl': '/etc/shadowsocks-libev/local.acl', 'plugin': '/usr/local/bin/v2ray-plugin','plugin_opts': 'server'}
        else:
            if obfs_type == 'tls':
                if vps_domain == '':
                    shadowsocks_config = {'server': ('[::0]', '0.0.0.0'),'server_port': port,'local_port': 1081,'mode': 'tcp_and_udp','key': key,'timeout': timeout,'method': method,'verbose': verbose,'ipv6_first': True, 'prefer_ipv6': prefer_ipv6,'fast_open': fast_open,'no_delay': no_delay,'reuse_port': reuse_port,'mptcp': mptcp,'ebpf': ebpf,'acl': '/etc/shadowsocks-libev/local.acl', 'plugin': '/usr/local/bin/obfs-server','plugin_opts': 'obfs=tls;mptcp;fast-open;t=400'}
                else:
                    shadowsocks_config = {'server': ('[::0]', '0.0.0.0'),'server_port': port,'local_port': 1081,'mode': 'tcp_and_udp','key': key,'timeout': timeout,'method': method,'verbose': verbose,'ipv6_first': True, 'prefer_ipv6': prefer_ipv6,'fast_open': fast_open,'no_delay': no_delay,'reuse_port': reuse_port,'mptcp': mptcp,'ebpf': ebpf,'acl': '/etc/shadowsocks-libev/local.acl', 'plugin': '/usr/local/bin/obfs-server','plugin_opts': 'obfs=tls;mptcp;fast-open;t=400;host=' + vps_domain}
            else:
                shadowsocks_config = {'server': ('[::0]', '0.0.0.0'),'server_port': port,'local_port': 1081,'mode': 'tcp_and_udp','key': key,'timeout': timeout,'method': method,'verbose': verbose,'ipv6_first': True, 'prefer_ipv6': prefer_ipv6,'fast_open': fast_open,'no_delay': no_delay,'reuse_port': reuse_port,'mptcp': mptcp,'ebpf': ebpf,'acl': '/etc/shadowsocks-libev/local.acl', 'plugin': '/usr/local/bin/obfs-server','plugin_opts': 'obfs=http;mptcp;fast-open;t=400'}
    else:
        shadowsocks_config = {'server': ('[::0]', '0.0.0.0'),'server_port': port,'local_port': 1081,'mode': 'tcp_and_udp','key': key,'timeout': timeout,'method': method,'verbose': verbose,'ipv6_first': True, 'prefer_ipv6': prefer_ipv6,'fast_open': fast_open,'no_delay': no_delay,'reuse_port': reuse_port,'mptcp': mptcp,'ebpf': ebpf,'acl': '/etc/shadowsocks-libev/local.acl'}

    if ordered(data) != ordered(json.loads(json.dumps(shadowsocks_config))):
        with open('/etc/shadowsocks-libev/config.json','w') as outfile:
            json.dump(shadowsocks_config,outfile,indent=4)
        os.system("systemctl restart shadowsocks-libev-server@config.service")
        for x in range (1,os.cpu_count()):
            os.system("systemctl restart shadowsocks-libev-server@config" + str(x) + ".service")
        shorewall_add_port(str(port),'tcp','shadowsocks')
        shorewall_add_port(str(port),'udp','shadowsocks')
        set_lastchange()
        return jsonify({'result': 'done','reason': 'changes applied','route': 'shadowsocks'})
    else:
        return jsonify({'result': 'done','reason': 'no changes','route': 'shadowsocks'})

# Set shorewall config
@app.route('/shorewall', methods=['POST'])
@jwt_required
def shorewall():
    params = request.get_json()
    state = params.get('redirect_ports', None)
    if state is None:
        return jsonify({'result': 'error','reason': 'Invalid parameters','route': 'shorewall'})
    initial_md5 = hashlib.md5(file_as_bytes(open('/etc/shorewall/rules', 'rb'))).hexdigest()
    fd, tmpfile = mkstemp()
    with open('/etc/shorewall/rules','r') as f, open(tmpfile,'a+') as n:
        for line in f:
            if state == 'enable' and line == '#DNAT		net		vpn:$OMR_ADDR	tcp	1-64999\n':
                n.write(line.replace(line[:1], ''))
            elif state == 'enable' and line == '#DNAT		net		vpn:$OMR_ADDR	udp	1-64999\n':
                n.write(line.replace(line[:1], ''))
            elif state == 'disable' and line == 'DNAT		net		vpn:$OMR_ADDR	tcp	1-64999\n':
                n.write('#' + line)
            elif state == 'disable' and line == 'DNAT		net		vpn:$OMR_ADDR	udp	1-64999\n':
                n.write('#' + line)
            else:
                n.write(line)
    os.close(fd)
    move(tmpfile,'/etc/shorewall/rules')
    final_md5 = hashlib.md5(file_as_bytes(open('/etc/shorewall/rules', 'rb'))).hexdigest()
    if not initial_md5 == final_md5:
        os.system("systemctl -q reload shorewall")
    # Need to do the same for IPv6...
    return jsonify({'result': 'done','reason': 'changes applied'})

@app.route('/shorewalllist', methods=['POST'])
@jwt_required
def shorewall_list():
    params = request.get_json()
    name = params.get('name', None)
    if name is None:
        return jsonify({'result': 'error','reason': 'Invalid parameters','route': 'shorewalllist'})
    fwlist = []
    with open('/etc/shorewall/rules','r') as f:
        for line in f:
            if '# OMR ' + name in line:
                fwlist.append(line)
    return jsonify({'list': fwlist})

@app.route('/shorewallopen', methods=['POST'])
@jwt_required
def shorewall_open():
    params = request.get_json()
    name = params.get('name', None)
    port = params.get('port', None)
    proto = params.get('proto', None)
    fwtype = params.get('fwtype', None)
    if name is None:
        return jsonify({'result': 'error','reason': 'Invalid parameters','route': 'shorewalllist'})
    shorewall_add_port(str(port),proto,name,fwtype)
    return jsonify({'result': 'done','reason': 'changes applied'})

@app.route('/shorewallclose', methods=['POST'])
@jwt_required
def shorewall_close():
    params = request.get_json()
    name = params.get('name', None)
    port = params.get('port', None)
    proto = params.get('proto', None)
    fwtype = params.get('fwtype', None)
    if name is None:
        return jsonify({'result': 'error','reason': 'Invalid parameters','route': 'shorewalllist'})
    shorewall_del_port(str(port),proto,name,fwtype)
    return jsonify({'result': 'done','reason': 'changes applied'})

# Set MPTCP config
@app.route('/mptcp', methods=['POST'])
@jwt_required
def mptcp():
    params = request.get_json()
    checksum = params.get('checksum', None)
    path_manager = params.get('path_manager', None)
    scheduler = params.get('scheduler', None)
    syn_retries = params.get('syn_retries', None)
    congestion_control = params.get('congestion_control', None)
    if not checksum or not path_manager or not scheduler or not syn_retries or not congestion_control:
        return jsonify({'result': 'error','reason': 'Invalid parameters','route': 'mptcp'})
    os.system('sysctl -qw net.mptcp.mptcp_checksum=' + checksum)
    os.system('sysctl -qw net.mptcp.mptcp_path_manager=' + path_manager)
    os.system('sysctl -qw net.mptcp.mptcp_scheduler=' + scheduler)
    os.system('sysctl -qw net.mptcp.mptcp_syn_retries=' + syn_retries)
    os.system('sysctl -qw net.ipv4.tcp_congestion_control=' + congestion_control)
    set_lastchange()
    return jsonify({'result': 'done','reason': 'changes applied'})

# Set global VPN config
@app.route('/vpn', methods=['POST'])
@jwt_required
def vpn():
    params = request.get_json()
    vpn = params.get('vpn', None)
    if not vpn:
        return jsonify({'result': 'error','reason': 'Invalid parameters','route': 'vpn'})
    os.system('echo ' + vpn + ' > /etc/openmptcprouter-vps-admin/current-vpn')
    set_lastchange()
    return jsonify({'result': 'done','reason': 'changes applied'})


# Set Glorytun config
@app.route('/glorytun', methods=['POST'])
@jwt_required
def glorytun():
    params = request.get_json()
    key = params.get('key', None)
    port = params.get('port', None)
    chacha = params.get('chacha', True)
    if not key or port is None:
        return jsonify({'result': 'error','reason': 'Invalid parameters','route': 'glorytun'})
    initial_md5 = hashlib.md5(file_as_bytes(open('/etc/glorytun-tcp/tun0', 'rb'))).hexdigest()
    with open('/etc/glorytun-tcp/tun0.key','w') as outfile:
        outfile.write(key)
    with open('/etc/glorytun-udp/tun0.key','w') as outfile:
        outfile.write(key)
    fd, tmpfile = mkstemp()
    with open('/etc/glorytun-tcp/tun0','r') as f, open(tmpfile,'a+') as n:
        for line in f:
            if 'PORT=' in line:
                n.write('PORT=' + str(port) + '\n')
            elif 'OPTIONS=' in line:
                if chacha:
                    n.write('OPTIONS="chacha20 retry count -1 const 5000000 timeout 90000 keepalive count 5 idle 10 interval 2 buffer-size 65536 multiqueue"\n')
                else:
                    n.write('OPTIONS="retry count -1 const 5000000 timeout 90000 keepalive count 5 idle 10 interval 2 buffer-size 65536 multiqueue"\n')
            else:
                n.write(line)
    os.close(fd)
    move(tmpfile,'/etc/glorytun-tcp/tun0')
    final_md5 = hashlib.md5(file_as_bytes(open('/etc/glorytun-tcp/tun0', 'rb'))).hexdigest()
    if not initial_md5 == final_md5:
        os.system("systemctl -q restart glorytun-tcp@tun0")
    initial_md5 = hashlib.md5(file_as_bytes(open('/etc/glorytun-udp/tun0', 'rb'))).hexdigest()
    fd, tmpfile = mkstemp()
    with open('/etc/glorytun-udp/tun0','r') as f, open(tmpfile,'a+') as n:
        for line in f:
            if 'BIND_PORT=' in line:
                n.write('BIND_PORT=' + str(port) + '\n')
            elif 'OPTIONS=' in line:
                if chacha:
                    n.write('OPTIONS="chacha persist"\n')
                else:
                    n.write('OPTIONS="persist"\n')
            else:
                n.write(line)
    os.close(fd)
    move(tmpfile,'/etc/glorytun-udp/tun0')
    final_md5 = hashlib.md5(file_as_bytes(open('/etc/glorytun-udp/tun0', 'rb'))).hexdigest()
    if not initial_md5 == final_md5:
        os.system("systemctl -q restart glorytun-udp@tun0")
    shorewall_add_port(str(port),'tcp','glorytun')
    set_lastchange()
    return jsonify({'result': 'done'})

# Set A Dead Simple VPN config
@app.route('/dsvpn', methods=['POST'])
@jwt_required
def dsvpn():
    params = request.get_json()
    key = params.get('key', None)
    port = params.get('port', None)
    if not key or port is None:
        return jsonify({'result': 'error','reason': 'Invalid parameters','route': 'dsvpn'})
    initial_md5 = hashlib.md5(file_as_bytes(open('/etc/dsvpn/dsvpn.key', 'rb'))).hexdigest()
    with open('/etc/dsvpn/dsvpn.key','w') as outfile:
        outfile.write(key)
    final_md5 = hashlib.md5(file_as_bytes(open('/etc/dsvpn/dsvpn.key', 'rb'))).hexdigest()
    if not initial_md5 == final_md5:
        os.system("systemctl -q restart dsvpn-server")
    shorewall_add_port(str(port),'tcp','dsvpn')
    set_lastchange()
    return jsonify({'result': 'done'})

# Set OpenVPN config
@app.route('/openvpn', methods=['POST'])
@jwt_required
def openvpn():
    params = request.get_json()
    key = params.get('key', None)
    if not key:
        return jsonify({'result': 'error','reason': 'Invalid parameters','route': 'openvpn'})
    initial_md5 = hashlib.md5(file_as_bytes(open('/etc/openvpn/server/static.key', 'rb'))).hexdigest()
    with open('/etc/openvpn/server/static.key','w') as outfile:
        outfile.write(base64.b64decode(key))
    final_md5 = hashlib.md5(file_as_bytes(open('/etc/openvpn/server/static.key', 'rb'))).hexdigest()
    if not initial_md5 == final_md5:
        os.system("systemctl -q restart openvpn@tun0")
    set_lastchange()
    return jsonify({'result': 'done'})

# Set WANIP
@app.route('/wan', methods=['POST'])
@jwt_required
def wan():
    params = request.get_json()
    ips = params.get('ips', None)
    if not ips:
        return jsonify({'result': 'error','reason': 'Invalid parameters','route': 'wan'})
    initial_md5 = hashlib.md5(file_as_bytes(open('/etc/shadowsocks-libev/local.acl', 'rb'))).hexdigest()
    with open('/etc/shadowsocks-libev/local.acl','w') as outfile:
        outfile.write('[white_list]\n')
        outfile.write(ips)
    final_md5 = hashlib.md5(file_as_bytes(open('/etc/shadowsocks-libev/local.acl', 'rb'))).hexdigest()
    #if not initial_md5 == final_md5:
        #os.system("systemctl restart shadowsocks-libev-server@config.service")
        #for x in range (1,os.cpu_count()):
            #os.system("systemctl restart shadowsocks-libev-server@config" + str(x) + ".service")

    return jsonify({'result': 'done'})

# Update VPS
@app.route('/update', methods=['GET'])
@jwt_required
def update():
    os.system("wget -O - http://www.openmptcprouter.com/server/debian9-x86_64.sh | sh")
    # Need to reboot if kernel change
    return jsonify({'result': 'done'})

# Backup
@app.route('/backuppost', methods=['POST'])
@jwt_required
def backuppost():
    params = request.get_json()
    backup_file = params.get('data', None)
    if not backup_file:
        return jsonify({'result': 'error','reason': 'Invalid parameters','route': 'backuppost'})
    with open('/var/opt/openmptcprouter/backup.tar.gz','wb') as f:
        f.write(base64.b64decode(backup_file))
    return jsonify({'result': 'done'})

@app.route('/backupget', methods=['GET'])
@jwt_required
def send_backup():
    with open('/var/opt/openmptcprouter/backup.tar.gz',"rb") as backup_file:
        file_base64 = base64.b64encode(backup_file.read())
        file_base64utf = file_base64.decode('utf-8')
    return jsonify({'data': file_base64utf})

@app.route('/backuplist', methods=['GET'])
@jwt_required
def list_backup():
    if os.path.isfile('/var/opt/openmptcprouter/backup.tar.gz'):
        modiftime = os.path.getmtime('/var/opt/openmptcprouter/backup.tar.gz')
        return jsonify({'backup': True, 'modif': modiftime})
    else:
        return jsonify({'backup': False})

@app.route('/backupshow', methods=['GET'])
@jwt_required
def show_backup():
    if os.path.isfile('/var/opt/openmptcprouter/backup.tar.gz'):
        router = OpenWrt(native=open('/var/opt/openmptcprouter/backup.tar.gz'))
        return jsonify({'backup': True,'data': router})
    else:
        return jsonify({'backup': False})

@app.route('/backupedit', methods=['POST'])
@jwt_required
def edit_backup():
    params = request.get_data()
    o = OpenWrt(params)
    o.write('backup',path='/var/opt/openmptcprouter/')
    return jsonify({'result': 'done'})


if __name__ == '__main__':
    with open('/etc/openmptcprouter-vps-admin/omr-admin-config.json') as f:
        omr_config_data = json.load(f)
    omrport=65500
    if 'port' in omr_config_data:
        omrport = omr_config_data["port"]
    app.run(host='0.0.0.0',port=omrport,ssl_context=('/etc/openmptcprouter-vps-admin/cert.pem','/etc/openmptcprouter-vps-admin/key.pem'),threaded=True)
